import requests
import json
import tkinter as tk
from tkinter import font as tkFont # Import font module
import psutil
import time

def get_ip_location():
    """
    Fetches the public IP address and its location using the ip-api.com service.
    Returns a formatted string with the info or an error message.
    """
    api_url = "http://ip-api.com/json/"
    try:
        response = requests.get(api_url, timeout=10)
        response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)

        try:
            data = response.json()
            # Check API status
            if data.get('status') == 'success':
                ip_address = data.get('query', 'N/A')
                city = data.get('city', 'N/A')
                country = data.get('country', 'N/A')
                return f"IP: {ip_address}\nCity: {city}\nCountry: {country}"
            else:
                error_message = data.get('message', 'Unknown API error')
                return f"API Error: {error_message}"

        except json.JSONDecodeError:
            return "Error: Failed to parse API response."
        except Exception as e:
            return f"Error processing data: {e}"

    except requests.exceptions.Timeout:
        return "Error: Request timed out."
    except requests.exceptions.ConnectionError:
        return "Error: Connection failed."
    except requests.exceptions.HTTPError as e:
        return f"Error: HTTP error - {e}"
    except requests.exceptions.RequestException as e:
        return f"Error: Request failed - {e}"

def show_overlay(info_text):
    """Displays the given text and network speed in a topmost, borderless overlay window."""
    root = tk.Tk()
    root.withdraw() # Hide the main window initially

    # Create a Toplevel window for the overlay
    overlay = tk.Toplevel(root)
    overlay.overrideredirect(True) # Remove window decorations (border, title bar)
    overlay.wm_attributes("-topmost", 1) # Keep window on top

    # --- Drag Logic ---
    def start_move(event):
        overlay.start_x = event.x
        overlay.start_y = event.y

    def do_move(event):
        new_x = overlay.winfo_x() + (event.x - overlay.start_x)
        new_y = overlay.winfo_y() + (event.y - overlay.start_y)
        overlay.geometry(f"+{new_x}+{new_y}")
    # --- End Drag Logic ---

    # --- Network Speed Logic ---
    def format_speed(speed_bytes_sec):
        if speed_bytes_sec < 1024:
            return f"{speed_bytes_sec:.1f} B/s"
        elif speed_bytes_sec < 1024 * 1024:
            return f"{speed_bytes_sec / 1024:.1f} KB/s"
        else:
            return f"{speed_bytes_sec / (1024 * 1024):.1f} MB/s"

    def update_display():
        try:
            current_time = time.time()
            current_counters = psutil.net_io_counters()
            current_sent = current_counters.bytes_sent
            current_recv = current_counters.bytes_recv

            if hasattr(overlay, 'last_time'): # Check if not the first run
                time_delta = current_time - overlay.last_time
                sent_delta = current_sent - overlay.last_sent
                recv_delta = current_recv - overlay.last_recv

                if time_delta > 0:
                    upload_speed = sent_delta / time_delta
                    download_speed = recv_delta / time_delta
                else:
                    upload_speed = 0
                    download_speed = 0

                up_str = format_speed(upload_speed)
                down_str = format_speed(download_speed)
                new_text = f"{info_text}\nUp: {up_str}\nDown: {down_str}"
            else: # First run
                new_text = f"{info_text}\nUp: Calculating...\nDown: Calculating..."

            label.config(text=new_text)

            # Store current stats for the next interval
            overlay.last_time = current_time
            overlay.last_sent = current_sent
            overlay.last_recv = current_recv

        except Exception as e:
            # Handle potential errors during update (e.g., psutil error)
            label.config(text=f"{info_text}\nError updating speed: {e}")
        finally:
            # Schedule the next update
            overlay.after(1000, update_display)

    # --- End Network Speed Logic ---

    # Configure appearance
    bg_color = "#333333" # Dark background
    fg_color = "red" # CHANGED: White text to Red text
    label_font = tkFont.Font(family="Consolas", size=10) # Use a monospaced font

    # Initial text before first speed calculation
    initial_display_text = f"{info_text}\nUp: Loading...\nDown: Loading..."

    label = tk.Label(
        overlay,
        text=initial_display_text,
        font=label_font,
        bg=bg_color,
        fg=fg_color,
        justify=tk.LEFT, # Align text to the left
        padx=10, # Padding inside the label
        pady=5
    )
    # Bind drag events to the label as well, so clicking text works
    label.bind("<ButtonPress-1>", start_move)
    label.bind("<B1-Motion>", do_move)
    label.pack()

    # --- Calculate initial position (revised) ---
    overlay.update_idletasks() # Ensure widgets are created
    screen_width = overlay.winfo_screenwidth()
    margin = 10
    y_pos = margin

    # 1. Place roughly first to trigger size calculation
    #    We can place it off-screen initially to avoid flicker if needed, or just near the edge.
    #    Let's try placing left edge at the target right edge first.
    initial_x_guess = screen_width - margin
    overlay.geometry(f"+{-9999}+{y_pos}") # Move off-screen left initially
    overlay.update_idletasks() # Update geometry calculations

    # 2. Get the actual width now that it's calculated
    actual_width = overlay.winfo_width()
    if actual_width <= 1: # Fallback if width calculation failed
        actual_width = 150 # Estimate a reasonable width

    # 3. Calculate the final X for the top-left corner to place the top-right correctly
    final_x_pos = screen_width - actual_width - margin

    # 4. Set the final position
    overlay.geometry(f"+{final_x_pos}+{y_pos}")
    # --- End Calculate initial position ---

    # Bind events to the overlay window itself
    overlay.bind("<ButtonPress-1>", start_move)
    overlay.bind("<B1-Motion>", do_move)
    # Right-click (<Button-3>) to close
    overlay.bind("<Button-3>", lambda e: root.destroy())
    # Also bind right-click to the label
    label.bind("<Button-3>", lambda e: root.destroy())

    # Start the update loop
    overlay.after(1000, update_display)

    root.mainloop() # Start the Tkinter event loop (for the hidden root)

if __name__ == "__main__":
    ip_info = get_ip_location()
    show_overlay(ip_info)
